#include <buola/scene/geodes/cpointcloud.h>
#include <buola/scene/techniques/cpointtechnique.h>

namespace buola { namespace scene {

CPointCloud::CPointCloud()
    :   mHaveData(false)
    ,   mTechnique(CPointTechnique::Get())
    ,   mTechniqueN(CPointNTechnique::Get())
{
}

CPointCloud::CPointCloud(cvi::CCloud_rgb8 &&pCloud)
    :   CPointCloud()
{
    SetCloud(std::move(pCloud));
}

CPointCloud::~CPointCloud()
{
    
}

void CPointCloud::SetCloud(cvi::CCloud_rgb8 &&pCloud)
{
    mCloud.reset(new cvi::CCloud_rgb8(std::move(pCloud)));
    mCloudN.reset();
    mHaveData=false;
}

void CPointCloud::SetCloud(cvi::CCloud_n_rgb8 &&pCloud)
{
    mCloudN.reset(new cvi::CCloud_n_rgb8(std::move(pCloud)));
    mCloud.reset();
    mHaveData=false;
}

void CPointCloud::Render(CRenderContext &pContext)
{
    if(!mHaveData)
        GenerateData();
    
    if(mCloudN)
        mTechniqueN->Prepare(pContext);
    else
        mTechnique->Prepare(pContext);

    glBindBuffer(GL_ARRAY_BUFFER,mVertexData.GetVBO());
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);
    mVertexData.SetVertexAttrib(0,FIELD_POSITION);
    mVertexData.SetVertexAttrib(1,FIELD_AMBIENT);
    mVertexData.SetVertexAttrib(2,FIELD_NORMAL);

    glDisable(GL_BLEND);
    glDrawArrays(GL_POINTS,0,mPointCount);

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);
}

void CPointCloud::GenerateData()
{
    if(mHaveData) return;
    
    if(!mVertexData)
    {
        mVertexData.AddField<float>(FIELD_POSITION,3);
        mVertexData.AddField<float>(FIELD_NORMAL,3);
        mVertexData.AddField<float>(FIELD_AMBIENT,4);
        
        mVertexData.Resize(400000);//mCloud.size());
    }

    struct ATTRIBUTE_PACKED LVertex
    {
        float x,y,z;
        float nx,ny,nz;
        float r,g,b,a;
    };
    
    LVertex *lVertices=mVertexData.Map<LVertex>();
    
    if(mCloud)
    {
        const auto &lCloud=*mCloud;
        
        for(int i=0;i<lCloud.size();i++)
        {
            lVertices[i].x=lCloud[i].mPoint.x();
            lVertices[i].y=lCloud[i].mPoint.y();
            lVertices[i].z=lCloud[i].mPoint.z();

            lVertices[i].nx=0;
            lVertices[i].ny=0;
            lVertices[i].nz=0;
            
            lVertices[i].r=lCloud[i].mPixel[0]/255.0;
            lVertices[i].g=lCloud[i].mPixel[1]/255.0;
            lVertices[i].b=lCloud[i].mPixel[2]/255.0;
            lVertices[i].a=1.0;
        }
        
        mPointCount=lCloud.size();
    }
    else if(mCloudN)
    {
        const auto &lCloud=*mCloudN;

        for(int i=0;i<lCloud.size();i++)
        {
            lVertices[i].x=lCloud[i].mPoint.x();
            lVertices[i].y=lCloud[i].mPoint.y();
            lVertices[i].z=lCloud[i].mPoint.z();

            lVertices[i].nx=lCloud[i].mNormal.x();
            lVertices[i].ny=lCloud[i].mNormal.y();
            lVertices[i].nz=lCloud[i].mNormal.z();
            
            lVertices[i].r=1;
            lVertices[i].g=1;
            lVertices[i].b=1;
            lVertices[i].a=1.0;
        }
        
        mPointCount=lCloud.size();
    }
    
    mVertexData.Unmap();
    
    mHaveData=true;
}

/*namespace scene*/ } /*namespace buola*/ }
